﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace GameLibrary.Settlement
{
    using Interfaces;
    using Events;
    using Exceptions;

    /// <summary>
    /// Represent city and village objects on the map
    /// </summary>
    [Serializable]
    public class Settlement:
        AbstractMapObject,
        ISettlement
    {
        #region type

        public SettlementFlags Type { get; private set; }
        internal void ChangeToAnotherType(SettlementFlags newType)
        {
            if (newType != this.Type)
                if (newType == SettlementFlags.Nature)
                    this.Buildings.Clear();
                else
                {
                    this.Buildings.Remove(this.Type);
                    if (this.Type == SettlementFlags.City)
                        this.Villages = new Dictionary<uint, Settlement>();
                    else
                        this.Villages = null;
                }
            this.Type = newType;
        }

        #endregion

        #region buildings

        /// <summary>
        /// Building lists.
        /// </summary>
        private Buildings Buildings = new Buildings();

        public void AddBuilding(BuildingType building)
        {
            if (building.Availability.HasFlag(this.Type) == true)
                this.Buildings.IncreaseLevel(building);
        }

        public void RemoveBuilding(BuildingType building, uint level = 1)
        {
            this.Buildings.DecreaseLevel(building, level);
            OnBuildingLevelDecreased(building, level);
        }

        /// <summary>
        /// Return building level of building
        /// Return 0 if not exist
        /// </summary>
        /// <param name="building"></param>
        /// <returns></returns>
        public uint GetBuildingLevel(BuildingType building)
        {
            if (this.Buildings.ContainsKey(building) == true)
                return this.Buildings[building];
            else
                return 0;
        }

        /// <summary>
        /// Return next level building cost for a building
        /// Not thinking about terrain effect
        /// </summary>
        /// <param name="building"></param>
        /// <returns></returns>
        public int GetNextLevelBuildingCost(BuildingType building)
        {
            if (building.Availability.HasFlag(this.Type))
                return (int)Math.Pow(SettlementHelper.BuildingCostBase, this.GetBuildingLevel(building) + 1);
            else
                return int.MaxValue;
        }

        /// <summary>
        /// Get all buildings in the settlement
        /// </summary>
        /// <returns></returns>
        public IEnumerable<BuildingType> GetCurrentBuildings()
        {
            return this.Buildings.Keys;
        }

        #endregion

        #region subordinate

        /// <summary>
        /// Villages which send raw material into the city
        /// </summary>
        private Dictionary<uint, Settlement> Villages;

        public IEnumerable<Settlement> GetVillages()
        {
            return this.Villages.Values;
        }

        /// <summary>
        /// Clear all villages subordinate to the city
        /// </summary>
        public void ResetVillages()
        {
            if (this.Villages != null)
            {
                foreach (var a in this.Villages.Values)
                    a.SetSubordinateOf(this.OwnerID);
                this.Villages.Clear();
            }
        }

        public uint SubordinateOf { get; private set; }

        internal void SetSubordinateOf(uint cityID)
        {
            this.SubordinateOf = cityID;
        }

        /// <summary>
        /// Add village as subordinate to a city.
        /// </summary>
        /// <param name="village"></param>
        /// <returns></returns>
        public bool AddVillage(Settlement village)
        {
            if (village.Type == SettlementFlags.Village)
            {
                this.Villages.Add(village.ID, village);
                village.SetSubordinateOf(this.ID); 
                return true;
            }
            else
                return false;
        }

        public bool RemoveVillage(Settlement village)
        {
            if (this.Type == SettlementFlags.City)
            {
                if (this.Villages.ContainsKey(village.ID) == true)
                {
                    this.Villages[village.ID].SetSubordinateOf(0); // not controlled by this settlement
                    this.Villages.Remove(village.ID);
                    return true;
                }
                else
                    return false;
            }
            else
                return false;
        }

        public int VillageNumber
        {
            get
            {
                if (this.Villages != null)
                    return this.Villages.Count;
                else
                    return -1;
            }
        }

        #endregion

        #region Production and Storage

        /// <summary>
        /// income from all villages.
        /// </summary>
        public uint Income { get; private set; }
        public void SetIncome(uint income)
        {
            this.Income = income;
        }

        /// <summary>
        /// income from tile.
        /// </summary>
        public uint BaseProduction { get; private set; }

        /// <summary>
        /// Production of this settlement
        /// </summary>
        public uint Production { get; private set; }

        public void SetProduction(uint production)
        {
            this.Production = production;
        }

        /// <summary>
        /// storage show income balance of this settlement
        /// </summary>
        public long Storage { get; private set; }

        public void SetStorage(long g)
        {
            this.Storage = g;
        }

        #endregion

        #region Info

        public SettlementInfo Info
        {
            get { return new SettlementInfo(this); }
        }

        #endregion

        #region constructor

        public Settlement(
            Position location, 
            uint ownerID, 
            SettlementFlags type, 
            uint baseProduction
            )
            : base(location, ownerID)
        {            
            this.Type = type;
            if (this.Type == SettlementFlags.City)
                this.Villages = new Dictionary<uint, Settlement>();
            this.BaseProduction = baseProduction;
        }

        #endregion

        public override void SetOwnerID(uint ownerID)
        {
            uint oldOwnerID = this.OwnerID;
            base.SetOwnerID(ownerID);
            if (this.Type == SettlementFlags.City)
            {
                foreach (var a in Villages)
                    a.Value.SetSubordinateOf(oldOwnerID);
                this.Villages = new Dictionary<uint, Settlement>();
            }
            else
                this.SubordinateOf = oldOwnerID;
            base.OnOwnerChanged(oldOwnerID, ownerID);
        }

        #region next turn

        public void NextTurn()
        {
            #region Get production

            this.Production = SettlementHelper.GetProduction(this); // get production
            this.Storage += this.Production;

            #endregion
        }

        #endregion

        #region ICity Members

        public event EventHandler<BuildingLevelDecreasedEventArgs> BuildingLevelDecreased;
       
        protected virtual void OnBuildingLevelDecreased(BuildingType building, uint level)
        {
            EventHandler<BuildingLevelDecreasedEventArgs> h = this.BuildingLevelDecreased;
            if (h != null)
                h(this, new BuildingLevelDecreasedEventArgs(this, building, level));
         //   else
         //       throw new EventHandlerNullException("building level decreased null");
        }

        #endregion
    }
}
